package ms.unicorn.language.languagedef;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.text.html.parser.Entity;

import ms.unicorn.EPhantomAttribute;
import ms.unicorn.Relation;
import ms.unicorn.UnicornPackage;
import ms.unicorn.util.ReflectionUtil;
import ms.unicorn.util.TypeConvert;

import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.edit.provider.ItemPropertyDescriptor;

public class TagDefUtil {

	private static Map<String, EAttribute> builtinFields;
	private static Map<TagEntryDef, EPhantomAttribute> attributes;
	private static Map<String, EDataType> primitiveType;
	private static Map<EDataType, Object> propertyImages;
	private static EcorePackage ecorePackage;
	
	static {
		ecorePackage = (EcorePackage)EPackage.Registry.INSTANCE.getEPackage(EcorePackage.eNS_URI);
		
		builtinFields = new HashMap<String, EAttribute>(5);
		builtinFields.put("NAME", UnicornPackage.eINSTANCE.getElement_Name());
		builtinFields.put("TEXT", UnicornPackage.eINSTANCE.getElement_Text());
		builtinFields.put("DESCRIPTION", UnicornPackage.eINSTANCE.getElement_Description());
		builtinFields.put("STEREOTYPE", UnicornPackage.eINSTANCE.getElement_Stereotype());
		
		primitiveType = new HashMap<String, EDataType>();
		primitiveType.put("int", ecorePackage.getEInt());
		primitiveType.put("string", ecorePackage.getEString());
		primitiveType.put("date", ecorePackage.getEDate());
		primitiveType.put("float", ecorePackage.getEFloat());
		primitiveType.put("boolean", ecorePackage.getEBoolean());
		
		propertyImages = new HashMap<EDataType, Object>();
		propertyImages.put(ecorePackage.getEInt(), ItemPropertyDescriptor.INTEGRAL_VALUE_IMAGE);
		propertyImages.put(ecorePackage.getEString(), ItemPropertyDescriptor.TEXT_VALUE_IMAGE);
		propertyImages.put(ecorePackage.getEBoolean(), ItemPropertyDescriptor.BOOLEAN_VALUE_IMAGE);
		propertyImages.put(ecorePackage.getEFloat(), ItemPropertyDescriptor.REAL_VALUE_IMAGE);
		
		attributes = new HashMap<TagEntryDef, EPhantomAttribute>();
	}
	
	public static EAttribute getEAttribute(LabelSourceField field) {
		if (field.isBuiltIn())
			return builtinFields.get(field.getName());
		return attributes.get(field.getField());
	}
	
	public static EAttribute getEAttribute(TagEntryDef field) {
		return attributes.get(field);
	}
	
	public static EDataType getDataType(TagEntryDef entry) {
		return primitiveType.get(entry.getType());
	}
	
	public static void createEntityPhantomAttribute(ElementDef owner, TagDef fields) {
		for(TagEntryDef field: fields.getEntry())
			createPhantomAttribute(owner, field);
	}
	
	protected static void createPhantomAttribute(ElementDef owner, TagEntryDef field) {
		Class<?> containerClass = null;
		EClass containerEClass = null;
		if (owner instanceof EntityDef) {
			containerClass = Entity.class;
			containerEClass = UnicornPackage.eINSTANCE.getEntity();
		}
		else {
			containerClass = Relation.class;
			containerEClass = UnicornPackage.eINSTANCE.getRelation();
		}
			
			
		EPhantomAttributeImpl attr = new EPhantomAttributeImpl(field, containerClass, containerEClass);
		attributes.put(field, attr);
	}
	
	public static List<TagDef> getTagDefs(Object obj) {
		return ReflectionUtil.getPropertyValue(obj, "Tags", null);
	}

	public static TagDef getTagDef(Object eDef, String name) {
		return getTagDef(getTagDefs(eDef), name);
	}
	
	public static TagDef getTagDef(List<TagDef> tags, String name) {
		if (tags != null)
			for(TagDef tag: tags)
				if (tag.getName().equals(name))
					return tag;
		return null;
	}
	
	public static TagDef getFields(Object eDef) {
		return getTagDef(eDef, "fields");
	}
	
	public static TagDef getProperties(Object eDef) {
		return getTagDef(eDef, "properties");
	}

	public static List<TagEntryDef> getAllTagProperties(Object objDef) {
		List<TagEntryDef> entries;
		Object superElement = ReflectionUtil.getPropertyValue(objDef, "SuperElement", null);
		if (superElement != null) 
			entries = getAllTagProperties(superElement);
		else 
			entries = new ArrayList<TagEntryDef>();
		TagDef props = getProperties(objDef);
		if (props != null) entries.addAll(props.getEntry());
		return entries;
	}

	public static Object getDefaultImage(TagEntryDef fieldDef) {
		EDataType type = getDataType(fieldDef);
		Object image = propertyImages.get(type);
		return image != null ? image : ItemPropertyDescriptor.GENERIC_VALUE_IMAGE;
	}
	
	public static boolean hasDefaultValue(TagEntryDef def) {
		return def.getValue() != null;
	}
	
	public static <T> T getBasicDefaultValue(TagEntryDef def, T defaultValue) {
		return XbaseUtil.evaluate(def.getValue(), defaultValue);
	}
	
	public static void setProperties(Object obj, List<TagEntryDef> properties) {
		for (TagEntryDef prop: properties)
			if (prop.getRef() != null) {
				Object value = LanguageDefUtil.resolveDefinition(prop.getRef());
				if (value instanceof ColorDef)
					value = TypeConvert.toColor((ColorDef)value);
				ReflectionUtil.setPropertyValue(obj, prop.getName(), value);
			}
			else if (hasDefaultValue(prop)) {
				String name = prop.getName();
				Object value = getBasicDefaultValue(prop, null);
				if (value != null) {
					ReflectionUtil.setPropertyValue(obj, name, value);
				}
			}
	}

	public static TagEntryDef getTagProperty(Object def, String name) {
		TagDef props = getProperties(def);
		if (props != null)
			for(TagEntryDef entry: props.getEntry())
				if (entry.getName().equals(name))
					return entry;
		return null;
	}

	@SuppressWarnings("unchecked")
	public static <T> T getTagPropertyValue(TagEntryDef entry, T defaultValue) {
		if (entry.getRef() != null) {
			Object value = LanguageDefUtil.resolveDefinition(entry.getRef());
			return (T) value; 
		}
		if (hasDefaultValue(entry))
			return getBasicDefaultValue(entry, defaultValue);
		return defaultValue;
	}
	
	public static <T> T getTagPropertyValue(Object def, String name, T defaultValue) {
		TagEntryDef property = getTagProperty(def, name);
		return property != null ? getTagPropertyValue(property, defaultValue) : defaultValue;
	}
}
